﻿#pragma once
#include <Python.h>
#include <fmt/format.h>
#include <fmt/xchar.h>
#include <hdf5.h>

#include <cstdint>
#include <filesystem>
#include <fstream>
#include <iostream>

namespace fs = std::filesystem;
using std::cout, std::endl;

//xxxxxxxxxxxxxxxxxxx 测试项目 configs
const std::string myH5Dataset = "distances";
#if 0
fs::path source_file_path = __FILE__;
fs::path project_root = source_file_path.parent_path().parent_path().parent_path();
#endif

const fs::path myPyHomePath = fs::path(MYPYTHON3_HOME);
const auto myPyHomePsx = myPyHomePath.generic_string();

const fs::path mySrcRootPath{MY_SOURCE_ROOT};
const auto mySrcRootPsx = mySrcRootPath.generic_string();  // posix 兼容格式

const fs::path myBinRootPath{MY_BINARY_ROOT};
//const auto h5DumpPsx = (myBinRootPath / "distances.h5").generic_string();
const auto h5DumpPsx = (myBinRootPath / "distances.h5").generic_string();

//xxxxxxxxxxxxxxxxxxx format
#define MYINFOLN(...)          \
    ::fmt::print(__VA_ARGS__); \
    ::fmt::print("\n");

//xxxxxxxxxxxxxxxxxxx py machine 相关

///===== debug PyConfig value
inline int8_t PrtConfig(PyConfig* config, bool prt = false)
{
    if (prt)
    {
#define MYDBG_WCT_PTR(x) MYINFOLN(L"" #x ": {}", (x) != NULL ? x : L"NULL");
        MYINFOLN("----------------- python interpreter config");
        MYDBG_WCT_PTR(config->home);
        MYDBG_WCT_PTR(config->base_exec_prefix);
        MYDBG_WCT_PTR(config->base_executable);
        MYDBG_WCT_PTR(config->base_prefix);
        MYDBG_WCT_PTR(config->exec_prefix);
        MYDBG_WCT_PTR(config->executable);
        MYDBG_WCT_PTR(config->prefix);
        MYINFOLN(L"config.module_search_paths_set: {}", config->module_search_paths_set);
        MYINFOLN(L"config.site_import: {}", config->site_import);
        MYINFOLN(L"config.safe_path: {}", config->safe_path);
#undef MYDBG_WCT_PTR
        //------------------- debug module search path
        for (Py_ssize_t i = 0; i < config->module_search_paths.length; i++)
        {
            //std::wcout << "items[" << i << "]: " << std::wstring(config.module_search_paths.items[i]) << '\n';
            MYINFOLN(L"config.module_search_paths.items[{}]: {}", i, std::wstring(config->module_search_paths.items[i]));
        }
    }

    return 0;
}

//======= python machine broken
#define MY_PYSTATUS_BREAK(_failed, status) \
    if (PyStatus_Exception(status))        \
    {                                      \
        _failed = true;                    \
        break;                             \
    }

inline int8_t InitialPyMachine()
{
    //=============== python home, Lib, DLLs 相关设置
    //const std::string mpy_pylib_zip_path = (mpy_home_p / "python312.zip").generic_string();
    const std::string myPyDir_Lib = (myPyHomePath / "Lib").generic_string();
    const std::string myPyDir_DLLs = (myPyHomePath / "DLLs").generic_string();
    const std::string myPyDir_site = (myPyHomePath / "Lib/site-packages").generic_string();

    const std::string myPyDir_myPyMod = (mySrcRootPath / "python").generic_string();

    //------------------- 配置 PyConfig 结构体;
    PyStatus status;
    //初始化解释器; 隔离模式
    PyConfig* config = new PyConfig();
#if 0
	PyConfig_InitIsolatedConfig(config);
#else
    PyConfig_InitPythonConfig(config);
#endif

    bool _failed{false};
    do
    {
#if 10
        /*设置 PythonHome, 必须指定 */
        status = PyConfig_SetBytesString(config, &config->home, myPyHomePsx.c_str());
        MY_PYSTATUS_BREAK(_failed, status);
        //status = PyConfig_SetBytesString(config, &config->prefix, mpy_home.c_str());
        //MY_PYSTATUS_BREAK;

        /* 设置程序名称, before reading the configuration (decode byte string from the locale encoding).
        Implicitly preinitialize Python. */
        status = PyConfig_SetBytesString(config, &config->program_name, MYPROGRAME_NAME);
        MY_PYSTATUS_BREAK(_failed, status);

        /* Read all configuration at once */
        status = PyConfig_Read(config);
        MY_PYSTATUS_BREAK(_failed, status);
#endif
        config->module_search_paths_set = 1;
#if 0
        status = PyWideStringList_Append(&config->module_search_paths, Py_DecodeLocale(mpy_pylib_zip_path.c_str(), nullptr));
        MY_PYSTATUS_BREAK(_failed, status);
#endif
        status = PyWideStringList_Append(&config->module_search_paths, Py_DecodeLocale(myPyDir_Lib.c_str(), nullptr));
        MY_PYSTATUS_BREAK(_failed, status);
        status = PyWideStringList_Append(&config->module_search_paths, Py_DecodeLocale(myPyDir_DLLs.c_str(), nullptr));
        MY_PYSTATUS_BREAK(_failed, status);
        status = PyWideStringList_Append(&config->module_search_paths, Py_DecodeLocale(myPyDir_myPyMod.c_str(), nullptr));
        MY_PYSTATUS_BREAK(_failed, status);

        // pythonpath_env 是 wchar_t*
#if 0
        status = PyConfig_SetBytesString(config, &config->pythonpath_env, mpy_pylib_zip_path.c_str());
        MY_PYSTATUS_BREAK(_failed);
#endif
        config->site_import = 1;  // 有很大的影响
        config->safe_path = 0;    // 没有影响

        //-------------------------- 初始化 Python 解释器
        PrtConfig(config, true);
        status = Py_InitializeFromConfig(config);
        MY_PYSTATUS_BREAK(_failed, status);

    } while (0);

    if (_failed)
    {
        PyConfig_Clear(config);
        Py_ExitStatusException(status);
        return 0;
    }

    //--------------------------- 初始化 Python 解释器
    if (!Py_IsInitialized())
    {
        MYINFOLN("Initial python interpreter");
        Py_Initialize();
    }
    else
    {
        MYINFOLN("Python interpreter has been initialized");
    }

    return 0;
}

inline PyObject* callPythonCalculateDistances(const std::vector<Point>& points)
{
#if 0
    std::string site_packages_path{"vcpkg_installed/x64-windows/tools/python3/Lib/site-packages"};
    PyObject* pySitePkg_u8 = PyUnicode_FromString(site_packages_path.c_str());
#endif

    InitialPyMachine();

    // 打印当前的sys.path，确认是否包含你的模块路径
#if 0
	PyRun_SimpleString("import sys;print(sys.path)");
#endif

    // 导入 Python 模块
    PyObject* pModule = PyImport_ImportModule("math_lib");
    if (!pModule)
    {
        PyErr_Print();
        cout << "[ymy] import failed" << endl;
        return nullptr;
    }

    // 获取 Python 函数
    PyObject* pFunc = PyObject_GetAttrString(pModule, "calculate_distances_and_save");
    if (!pFunc || !PyCallable_Check(pFunc))
    {
        if (PyErr_Occurred()) PyErr_Print();
        return nullptr;
    }

    // 创建 Python 列表并添加点数据
    PyObject* pPoints = PyList_New(0);
    for (const auto& point : points)
    {
        PyObject* pPoint = PyList_New(2);
        PyList_SetItem(pPoint, 0, PyFloat_FromDouble(point.x));
        PyList_SetItem(pPoint, 1, PyFloat_FromDouble(point.y));
        PyList_Append(pPoints, pPoint);
        Py_DECREF(pPoint);
    }

    // 创建 Python 字符串对象作为 h5 路径参数
    PyObject* pH5Path = PyUnicode_FromString(h5DumpPsx.c_str());

    // 调用 Python 函数
    PyObject* pArgs = PyTuple_New(2);
    PyTuple_SetItem(pArgs, 0, pPoints);
    PyTuple_SetItem(pArgs, 1, pH5Path);
    PyObject* pResult = PyObject_CallObject(pFunc, pArgs);

    // 释放资源
    Py_DECREF(pArgs);
    Py_DECREF(pPoints);

    if (pResult == nullptr)
    {
        PyErr_Print();
    }

    return pResult;
}

std::vector<double> readPythonH5File(const std::string& filePath)
{
    // 读取 Python 生成的 .h5 文件
    hid_t file = H5Fopen(filePath.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
    hid_t dataset = H5Dopen2(file, myH5Dataset.c_str(), H5P_DEFAULT);
    hid_t dataspace = H5Dget_space(dataset);
    hsize_t dims[1];
    H5Sget_simple_extent_dims(dataspace, dims, NULL);
    std::vector<double> python_distances(dims[0]);
    H5Dread(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, python_distances.data());
    H5Dclose(dataset);
    H5Sclose(dataspace);
    H5Fclose(file);
    return python_distances;
}
